/*
Spotter Exercise Manager
Copyright 2011 Evan Nelson.

This file is part of Spotter Exercise Manager.

Spotter Exercise Manager is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Spotter Exercise Manager is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Spotter Exercise Manager.  If not, see <http://www.gnu.org/licenses/>.

The author can be contacted by emailing ean5533@gmail.com
*/

#include <QtDebug>
#include <QSqlQuery>
#include <QComboBox>
#include <QPlainTextEdit>
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <QMessageBox>
#include <QInputDialog>

#include "weightlog.h"
#include "ui_weightlog.h"

#include "mainwindow.h"
#include "editexercises.h"

#include "database.h"
#include "settings.h"
#include "delegate.h"

WeightLog::WeightLog(int logID, int mode, QWidget *parent)
    : QDialog(parent), ui(new Ui::WeightLogClass)
{
    ui->setupUi(this);
    QSqlQuery *query;

    if(mode == MODE_ADD || mode == MODE_ADD_TEMPLATE) {
        //need to generate a new logID
        QString sQuery;
        sQuery.append("INSERT INTO weightLog(dateRecorded, status) VALUES('new', 'Open')");

        query = Database::prepareQuery(sQuery);
        query->exec();
        delete query;

        //grab the new auto-generated logID
        query = Database::prepareQuery("SELECT LID FROM weightLog WHERE dateRecorded = 'new' ORDER BY LID DESC");
        query->exec();
        query->next();
        this->logID = query->value(0).toInt();
        delete query;
    } else {
        this->logID = logID;
    }

    this->mode = mode;

    ui->labelDateLog->setVisible(false);
    ui->dateLog->setVisible(false);
    ui->labelTemplateName->setVisible(false);
    ui->txtTemplateName->setVisible(false);

    //gather info about this log
    if(this->mode == MODE_ADD || this->mode == MODE_VIEW || this->mode == MODE_EDIT) {
        query = Database::prepareQuery("SELECT dateRecorded FROM WeightLog WHERE LID = " + QString::number(this->logID));

        query->exec();
        query->next();
        this->logDate = query->value(0).toString();
        delete query;

        QString status = (this->mode == MODE_ADD ? "Viewing" : "Editing");
        this->setWindowTitle(status + " Workout Log #" + QString::number(this->logID) + " (" + this->logDate + ")");

        //set the date box
        ui->labelDateLog->setVisible(true);
        ui->dateLog->setVisible(true);
        if(this->mode == MODE_ADD) {
            ui->dateLog->setDate(QDate::currentDate());
        } else {
            ui->dateLog->setDate(QDate::fromString(this->logDate, Settings::DATE_FORMAT));
        }
    } else if (this->mode == MODE_EDIT_TEMPLATE) {
        query = Database::prepareQuery("SELECT templateName FROM WeightLog WHERE LID = " + QString::number(this->logID));

        query->exec();
        query->next();
        this->templateName = query->value(0).toString();
        delete query;

        this->setWindowTitle("Editing template '" + templateName + "'");

        //set the template name box
        ui->labelTemplateName->setVisible(true);
        ui->txtTemplateName->setVisible(true);
        ui->txtTemplateName->setText(this->templateName);
    } else {
        this->templateName = "";

        this->setWindowTitle("Creating new template");

        ui->labelTemplateName->setVisible(true);
        ui->txtTemplateName->setVisible(true);
    }

    //set the buttonbox buttons for the form
    ui->buttonBox->clear();
    ui->buttonBox->addButton(QDialogButtonBox::Ok);
    if(this->mode == MODE_ADD || this->mode == MODE_EDIT || this->mode == MODE_ADD_TEMPLATE || this->mode == MODE_EDIT_TEMPLATE) {
        ui->buttonBox->addButton(QDialogButtonBox::Cancel);
        ui->chkCloseLog->setEnabled(true);
    }

    //set columns to autosize
    ui->tableLog->horizontalHeader()->setResizeMode(0, QHeaderView::ResizeToContents);
    ui->tableLog->horizontalHeader()->setResizeMode(1, QHeaderView::ResizeToContents);
    ui->tableLog->horizontalHeader()->setResizeMode(2, QHeaderView::ResizeToContents);
    ui->tableLog->horizontalHeader()->setResizeMode(3, QHeaderView::ResizeToContents);
    ui->tableLog->horizontalHeader()->setResizeMode(4, QHeaderView::ResizeToContents);
    ui->tableLog->horizontalHeader()->setResizeMode(5, QHeaderView::Stretch);

    if(this->mode == MODE_ADD_TEMPLATE || this->mode == MODE_EDIT_TEMPLATE) {
        //don't show weight, reps, or notes column for templates
        ui->tableLog->setColumnHidden(3, true);
        ui->tableLog->setColumnHidden(4, true);
        ui->tableLog->setColumnHidden(5, true);

        //set exercise name and weight type to equal widths
        ui->tableLog->horizontalHeader()->setResizeMode(1, QHeaderView::Stretch);
        ui->tableLog->horizontalHeader()->setResizeMode(2, QHeaderView::Stretch);
    }

    //build a list of Exercises
    rebuildExerciseList();

    //build a list of weight types
    query = Database::prepareQuery("SELECT weightType FROM WeightType ORDER BY weightType");
    query->exec();
    while(query->next()) {
        this->weightTypes.append(query->value(0).toString());
    }
    delete query;

    //hide "Order" column (only used for sorting)
    ui->tableLog->setColumnHidden(0, true);

    if(this->mode == MODE_EDIT || this->mode == MODE_ADD || this->mode == MODE_EDIT_TEMPLATE || this->mode == MODE_ADD_TEMPLATE) {
        //set up context menu on log table
        QAction* separator = new QAction(this);
        separator->setSeparator(true);
        ui->tableLog->addAction(ui->actionAdd_New_Row);
        ui->tableLog->addAction(ui->actionCopy_Row);
        ui->tableLog->addAction(ui->actionDelete_Row);
        ui->tableLog->addAction(separator);
        ui->tableLog->addAction(ui->actionAdd_new_exercise);
        ui->tableLog->setContextMenuPolicy(Qt::ActionsContextMenu);

        if(this->mode == MODE_EDIT_TEMPLATE || this->mode == MODE_ADD_TEMPLATE) {
            ui->chkCloseLog->hide();
        }

    } else {
        //hide close log checkbox, add, delete and copy buttons, and disable the date edit
        ui->chkCloseLog->hide();
        ui->panelEditButtons->hide();
        ui->dateLog->setEnabled(false);
    }

    refreshTable();
}

WeightLog::~WeightLog()
{
    delete ui;
}

void WeightLog::refreshTable() {
    ui->tableLog->clearContents();
    ui->tableLog->setRowCount(0);
    ui->tableLog->setSortingEnabled(false);

    ui->tableLog->setItemDelegateForColumn(1, new ComboBoxDelegate(this->exercises));
    ui->tableLog->setItemDelegateForColumn(2, new ComboBoxDelegate(this->weightTypes));
    ui->tableLog->setItemDelegateForColumn(3, new DoubleSpinBoxDelegate());
    ui->tableLog->setItemDelegateForColumn(4, new SpinBoxDelegate());

    QSqlQuery *query;
    query = Database::prepareQuery("SELECT exercise, weightType, weight, reps, notes FROM Weightlifting WHERE logID = " + QString::number(this->logID));
    query->exec();

    while(query->next()) {
        QString sEx = query->value(0).toString();
        QString sWt = query->value(1).toString();
        double nWeight = query->value(2).toDouble();
        int nReps = query->value(3).toInt();
        QString sNotes = query->value(4).toString();

        addRow(sEx, sWt, nWeight, nReps, sNotes);
    }

    //don't enable sorting if in editing or adding mode (too complicated with comboboxes)
    if(this->mode == MODE_VIEW) {
        ui->tableLog->setSortingEnabled(true);
        ui->tableLog->sortByColumn(0, Qt::AscendingOrder);
    }

    delete query;
}

void WeightLog::accept() {
    saveData();
    this->deleteLater();
}

void WeightLog::saveData() {
    //no data to save if just viewing a log
    if(this->mode == MODE_VIEW) return;

    //start by deleting any exercises associated with this log
    QSqlQuery *query = Database::prepareQuery("DELETE FROM Weightlifting WHERE logID = " + QString::number(this->logID));
    query->exec();
    delete query;

    for(int i = 0; i < ui->tableLog->rowCount(); i++) {
        int sequence = i+1;

        QString exercise = ui->tableLog->item(i, 1)->text();
        QString weightType = ui->tableLog->item(i, 2)->text();
        QString weight = ui->tableLog->item(i, 3)->text();
        QString reps = ui->tableLog->item(i, 4)->text();
        QString notes = ui->tableLog->item(i, 5)->text();

        if(!exercise.trimmed().isEmpty()) {
            QString sQuery = "INSERT INTO Weightlifting(logID, exercise, weightType, weight, reps, sequence, notes) VALUES("
                                                       + QString::number(this->logID)
                                                       + ", " + "'" + exercise + "'"
                                                       + ", " + "'" + weightType + "'"
                                                       + ", " + weight
                                                       + ", " + reps
                                                       + ", " + QString::number(sequence)
                                                       + ", " + "'" + notes + "'"
                                                       + ")";

            query = Database::prepareQuery(sQuery);
            query->exec();
            delete query;
        }
    }

    if(this->mode == MODE_ADD || this->mode == MODE_EDIT) {
        //Update log date
        this->logDate = ui->dateLog->date().toString(Settings::DATE_FORMAT);
        query = Database::prepareQuery("UPDATE WeightLog SET dateRecorded = '" + this->logDate + "'"
                                                   + " WHERE LID = " + QString::number(this->logID));
        query->exec();
        delete query;

        //Set log status to "Completed" if box is checked
        if(ui->chkCloseLog->isChecked()) {
            //TODO: validate that all fields are filled in correctly (especially weight and reps)
            query = Database::prepareQuery("UPDATE WeightLog SET status = 'Completed' WHERE LID = " + QString::number(this->logID));
            query->exec();
            delete query;
        }
    } else if(this->mode == MODE_ADD_TEMPLATE || this->mode == MODE_EDIT_TEMPLATE){
        //Update the template name
        this->templateName = ui->txtTemplateName->text().trimmed();
        query = Database::prepareQuery("UPDATE WeightLog SET"
                                                   "    dateRecorded = 'TEMPLATE',"
                                                   "    status = 'TEMPLATE',"
                                                   "    templateName = '" + this->templateName + "'"
                                                   " WHERE LID = " + QString::number(this->logID));
        query->exec();
        delete query;
    }
}

void WeightLog::reject() {
    if(this->mode == MODE_ADD || this->mode == MODE_ADD_TEMPLATE) {
        //we need to delete the new log we just created
        QSqlQuery *query = Database::prepareQuery("DELETE FROM weightLog WHERE LID = " + QString::number(this->logID));
        query->exec();
        delete query;
    }
    this->deleteLater();
}

void WeightLog::addRow(QString sExercise = "", QString sWeightType = "", double nWeight = 0, int nReps = 0, QString sNotes = "") {
    int row = ui->tableLog->rowCount();

    ui->tableLog->setRowCount(row+1);

    //set up edit boxes for fields if editting
    if(this->mode == MODE_EDIT || this->mode == MODE_ADD || this->mode == MODE_EDIT_TEMPLATE || this->mode == MODE_ADD_TEMPLATE) {
        ui->tableLog->setItem(row, 1, new QTableWidgetItem(sExercise));
        ui->tableLog->setItem(row, 2, new QTableWidgetItem(sWeightType));
        ui->tableLog->setItem(row, 3, new QTableWidgetItem(QString::number(nWeight)));
        ui->tableLog->setItem(row, 4, new QTableWidgetItem(QString::number(nReps)));
        ui->tableLog->setItem(row, 5, new QTableWidgetItem(sNotes));
    } else {
        ui->tableLog->setEditTriggers(QAbstractItemView::NoEditTriggers);
        ui->tableLog->setItem(row, 0, new QTableWidgetItem(QString("%1").arg(row + 1, 3)));
        ui->tableLog->setItem(row, 1, new QTableWidgetItem(sExercise));
        ui->tableLog->setItem(row, 2, new QTableWidgetItem(sWeightType));
        ui->tableLog->setItem(row, 3, new QTableWidgetItem(QString::number(nWeight)));
        ui->tableLog->setItem(row, 4, new QTableWidgetItem(QString::number(nReps)));
        ui->tableLog->setItem(row, 5, new QTableWidgetItem(sNotes));
    }
}

void WeightLog::deleteRow() {
    int row = ui->tableLog->currentRow();
    ui->tableLog->removeRow(row);
}

void WeightLog::copyRow(int row) {
    QString exercise = ui->tableLog->item(row, 1)->text();
    QString weightType = ui->tableLog->item(row, 2)->text();
    double weight = ui->tableLog->item(row, 3)->text().toDouble();
    int reps = ui->tableLog->item(row, 4)->text().toInt();
    QString notes = ui->tableLog->item(row, 5)->text();

    addRow(exercise, weightType, weight, reps, notes);
}

void WeightLog::on_btnAdd_clicked()
{
    addRow();
    ui->tableLog->scrollToBottom();
}

void WeightLog::on_btnDelete_clicked()
{
    deleteRow();
}

void WeightLog::on_btnCopy_clicked()
{
    copyRow(ui->tableLog->currentRow());
}

void WeightLog::on_dateLog_editingFinished()
{
    this->logDate = ui->dateLog->date().toString(Settings::DATE_FORMAT);
}

void WeightLog::rebuildExerciseList()
{
    this->exercises.clear();

    QSqlQuery* query = Database::prepareQuery("SELECT exercise FROM Exercise WHERE primaryType = 'Weightlifting' ORDER BY exercise");
    query->exec();
    while(query->next()) {
        this->exercises.append(query->value(0).toString());
    }
    delete query;
}

void WeightLog::on_actionAdd_new_exercise_triggered()
{
    //get exercise name from user
    bool ok;
    QString exerciseName = QInputDialog::getText(this, "New exercise", "Enter new exercise name:", QLineEdit::Normal, QString::null, &ok).trimmed();
    if(!ok || exerciseName.isEmpty()) return;

    //add exercise to database
    EditExercises::insertNewExercise(exerciseName, EditExercises::MODE_WT);

    //rebuild combobox delegate
    rebuildExerciseList();
    ui->tableLog->setItemDelegateForColumn(1, new ComboBoxDelegate(this->exercises));
}

void WeightLog::on_actionAdd_New_Row_triggered()
{
    addRow();
    ui->tableLog->scrollToBottom();
}

void WeightLog::on_actionCopy_Row_triggered()
{
    copyRow(ui->tableLog->currentRow());
}

void WeightLog::on_actionDelete_Row_triggered()
{
    deleteRow();
}

void WeightLog::on_btnLoadTemplate_clicked()
{
    //get a list of templates to use
    QStringList templates;
    QSqlQuery* query = Database::prepareQuery("SELECT templateName FROM WeightLog WHERE templateName IS NOT NULL ORDER BY templateName");
    query->exec();
    while(query->next()) {
        templates.append(query->value(0).toString());
    }
    delete query;

    if(templates.count() < 1) {
        QMessageBox("No templates", "You have not created any templates.", QMessageBox::Warning, QMessageBox::NoButton, QMessageBox::NoButton, QMessageBox::Ok).exec();
        return;
    }

    bool ok;
    QString templateName = QInputDialog::getItem(this, "Load template data", "Please choose a template to load (this will overwrite all existing data in the log)", templates, 0, false, &ok);
    if(!ok) return;

    //remove all data from the table and fill in rows from the template selected
    ui->tableLog->clearContents();
    ui->tableLog->setRowCount(0);
    ui->tableLog->setSortingEnabled(false);

    ui->tableLog->setItemDelegateForColumn(1, new ComboBoxDelegate(this->exercises));
    ui->tableLog->setItemDelegateForColumn(2, new ComboBoxDelegate(this->weightTypes));
    ui->tableLog->setItemDelegateForColumn(3, new DoubleSpinBoxDelegate());
    ui->tableLog->setItemDelegateForColumn(4, new SpinBoxDelegate());

    query = Database::prepareQuery("SELECT w.exercise, w.weightType, w.weight, w.reps, w.notes "
                                               " FROM Weightlifting w"
                                               " JOIN Weightlog l ON w.logID = l.LID"
                                               " WHERE l.templateName = '" + templateName + "'");
    query->exec();
    while(query->next()) {
        QString sEx = query->value(0).toString();
        QString sWt = query->value(1).toString();
        double nWeight = query->value(2).toDouble();
        int nReps = query->value(3).toInt();
        QString sNotes = query->value(4).toString();

        addRow(sEx, sWt, nWeight, nReps, sNotes);
    }
    delete query;
}

void WeightLog::on_btnMoveDown_clicked()
{
    if(ui->tableLog->currentRow() < 0 || ui->tableLog->currentRow() == ui->tableLog->rowCount() - 1) return;
    moveRow(ui->tableLog, false);
    ui->tableLog->setCurrentCell(ui->tableLog->currentRow()+1, ui->tableLog->currentColumn());
}

void WeightLog::on_btnMoveUp_clicked()
{
    if(ui->tableLog->currentRow() < 1) return;
    moveRow(ui->tableLog, true);
    ui->tableLog->setCurrentCell(ui->tableLog->currentRow()-1, ui->tableLog->currentColumn());
}

void WeightLog::moveRow(QTableWidget* table, bool up)
{
        Q_ASSERT(table->selectedItems().count() > 0);
        const int sourceRow = table->row(table->selectedItems().at(0));
        const int destRow = (up ? sourceRow-1 : sourceRow+1);
        Q_ASSERT(destRow >= 0 && destRow < table->rowCount());

        // take whole rows
        QList<QTableWidgetItem*> sourceItems = takeRow(table, sourceRow);
        QList<QTableWidgetItem*> destItems = takeRow(table, destRow);

        // set back in reverse order
        setRow(table, sourceRow, destItems);
        setRow(table, destRow, sourceItems);
}

// takes and returns the whole row
QList<QTableWidgetItem*> WeightLog::takeRow(QTableWidget *table, int row)
{
        QList<QTableWidgetItem*> rowItems;
        for (int col = 0; col < table->columnCount(); ++col)
        {
                rowItems << table->takeItem(row, col);
        }
        return rowItems;
}

// sets the whole row
void WeightLog::setRow(QTableWidget* table, int row, const QList<QTableWidgetItem*>& rowItems)
{
        for (int col = 0; col < table->columnCount(); ++col)
        {
                table->setItem(row, col, rowItems.at(col));
        }
}
